﻿// =====================================================================
//  File:       CrmServiceHelper.cs
//  Summary:    This sample provides class methods that handle 
//              authentication with the CRM server and organization
//              discovery.
// =====================================================================
//
//  This file is part of the Microsoft Dynamics CRM SDK code samples.
//
//  Copyright (C) Microsoft Corporation.  All rights reserved.
//
//  This source code is intended only as a supplement to Microsoft
//  Development Tools and/or on-line documentation.  See these other
//  materials for detailed information regarding Microsoft code samples.
//
//  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//
// =====================================================================
//<snippetCrmServiceHelper>
using System;
using System.Xml;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;

// These namespaces are found in the Microsoft.Xrm.Sdk.dll assembly
// found in the SDK\bin folder.
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Discovery;

namespace RibbonEditor
{
    public class CrmServiceHelper
    {
        /// <summary>
        /// Discovers the organizations that the calling user belongs to.
        /// </summary>
        /// <param name="service">A discovery service proxy instance.</param>
        /// <returns>Array containing information on each organization.</returns>
        /// <seealso cref="Connect"/>
        static public OrganizationDetailCollection DiscoverOrganizations(IDiscoveryService service)
        {
            RetrieveOrganizationsRequest orgRequest = new RetrieveOrganizationsRequest();
            RetrieveOrganizationsResponse orgResponse =
                (RetrieveOrganizationsResponse)service.Execute(orgRequest);

            return orgResponse.Details;
        }

        /// <summary>
        /// Finds a specific organization detail in the array of organization details
        /// returned from the discovery service.
        /// </summary>
        /// <param name=orgFriendlyName">The friendly name of the organization to find.</param>
        /// <param name="orgDetails">Array of organization detail object returned from the discovery service.</param>
        /// <returns>Organization details or null if the organization was not found.</returns>
        /// <seealso cref="DiscoveryOrganizations"/>
        static public OrganizationDetail FindOrganization(string orgFriendlyName, OrganizationDetailCollection orgDetails)
        {
            OrganizationDetail orgDetail = null;

            foreach (OrganizationDetail detail in orgDetails)
            {
                if (String.Compare(detail.FriendlyName, orgFriendlyName) == 0)
                {
                    orgDetail = detail;
                    break;
                }
            }

            return orgDetail;
        }

        /// <summary>
        /// Retrieve specific organization details and 
        /// returns IOrganizationService using claims authentication.
        /// </summary>
        /// <param name="organizationFriendlyName">The friendly name of the target organization.</param>
        /// <param name="discoveryServerAddress">The Web address of the discovery server.</param>
        /// <returns>An organization service proxy instance.</returns>
        /// <example>IOrganizationService service  = Connect("Contoso", "microsoft.com:5555"</example>
        static public IOrganizationService Connect(string organizationFriendlyName, string discoveryServerAddress)
        {
            return Connect(organizationFriendlyName, discoveryServerAddress, false);
        }

        /// <summary>
        /// Retrieve specific organization details and 
        /// returns IOrganizationService using claims authentication with increase the maximum number of objects in the graph.
        /// </summary>
        /// <param name="organizationFriendlyName">The friendly name of the target organization.</param>
        /// <param name="discoveryServerAddress">The Web address of the discovery server.</param>
        /// <param name="endpointToMax">Increase the maximum number of objects in the graph.</param>
        /// <returns>An organization service proxy instance.</returns>
        /// <example>IOrganizationService service  = Connect("Contoso", "microsoft.com:5555", true)</example>
        static public IOrganizationService Connect(string organizationFriendlyName, string discoveryServerAddress, bool endpointToMax)
        {
            String strOrganizationServiceUrl = String.Empty;

            // Create and configure an IDiscoveryService instance for accessing the discovery service. 
            IServiceConfiguration<IDiscoveryService> dinfo =
                ServiceConfigurationFactory.CreateConfiguration<IDiscoveryService>(
                    new Uri(String.Format("http://{0}/XRMServices/2011/Discovery.svc", discoveryServerAddress)));

            var dchannelFactory = dinfo.CreateChannelFactory(ClientAuthenticationType.Kerberos);

            IDiscoveryService discoveryService = dchannelFactory.CreateChannel();
            // Obtain organization information from the discovery service. 
            if (discoveryService != null)
            {
                // Obtain information about the organizations that the system user belongs to.
                OrganizationDetailCollection orgs = DiscoverOrganizations(discoveryService);

                // Located the information on the specified organization.
                OrganizationDetail orgDetail = FindOrganization(organizationFriendlyName, orgs);

                strOrganizationServiceUrl = orgDetail.Endpoints[EndpointType.OrganizationService];
            }
            else
                return null;

            // Create and configure an IOrganizationService instance for accessing the organization service.
            IServiceConfiguration<IOrganizationService> oinfo =
                ServiceConfigurationFactory.CreateConfiguration<IOrganizationService>(
                    new Uri(strOrganizationServiceUrl));

            System.ServiceModel.Description.ServiceEndpoint endpoint = oinfo.CurrentServiceEndpoint;

            if (endpointToMax)
            {
                //Loop through the behaviors for the endpoint and increase the maximum number of objects in the graph
                foreach (OperationDescription operation in endpoint.Contract.Operations)
                {
                    //Retrieve the behavior for the operator
                    DataContractSerializerOperationBehavior serializerBehavior =
                        operation.Behaviors.Find<DataContractSerializerOperationBehavior>();
                    if (null != serializerBehavior)
                    {
                        serializerBehavior.MaxItemsInObjectGraph = int.MaxValue;
                    }
                }
            }

            endpoint.Behaviors.Add(new ProxyTypesBehavior());


            if (endpointToMax)
            {
                //Create a new binding element from the endpoint's binding so that certain properties can be changed generically
                XrmBinding newBinding = new XrmBinding(endpoint.Binding);
                endpoint.Binding = newBinding;

                //Set the required properties
                newBinding.MaxReceivedMessageSize = 1024 * 1024 * 20; //10 MB
                newBinding.MaxBufferSize = 1024 * 1024 * 20; //10 MB

                //Set the properties on the reader quotas
                newBinding.ReaderQuotas.MaxStringContentLength = 20 * 1024 * 1024; //10 MB
                newBinding.ReaderQuotas.MaxArrayLength = int.MaxValue;
                newBinding.ReaderQuotas.MaxBytesPerRead = int.MaxValue;
            }

            var ochannelFactory = oinfo.CreateChannelFactory(ClientAuthenticationType.Kerberos);
            return ochannelFactory.CreateChannel();
        }
    }

    #region Internal Classes

    /// <summary>
    /// Remove the SolutionComponentType enum class as soon as 
    /// it is available in the strong type library.
    /// </summary>
    public static class SolutionComponentType
    {
        public const int Entity = 1;
        public const int Attribute = 2;
        public const int Relationship = 3;
        public const int AttributePicklistValue = 4;
        public const int AttributeLookupValue = 5;
        public const int ViewAttribute = 6;
        public const int LocalizedLabel = 7;
        public const int RelationshipExtraCondition = 8;
        public const int OptionSet = 9;
        public const int EntityRelationship = 10;
        public const int EntityRelationshipRole = 11;
        public const int EntityRelationshipRelationships = 12;
        public const int Role = 20;
        public const int RolePrivileges = 21;
        public const int DisplayString = 22;
        public const int DisplayStringMap = 23;
        public const int OrganizationUI = 24;
        public const int Organization = 25;
        public const int SavedQuery = 26;
        public const int PluginType = 27;
        public const int PluginAssembly = 28;
        public const int Workflow = 29;
        public const int WorkflowDependency = 30;
        public const int Template = 36;
        public const int ContractTemplate = 37;
        public const int KbArticleTemplate = 38;
        public const int MailMergeTemplate = 39;
        public const int DuplicateRule = 44;
        public const int DuplicateRuleCondition = 45;
        public const int EntityMap = 46;
        public const int AttributeMap = 47;
        public const int RibbonCommand = 48;
        public const int RibbonContextGroup = 49;
        public const int RibbonCustomization = 50;
        public const int RibbonRule = 52;
        public const int RibbonTabToCommandMap = 53;
        public const int RibbonDiff = 55;
        public const int SavedQueryVisualization = 59;
        public const int SystemForm = 60;
        public const int WebResource = 61;
        public const int SiteMap = 62;
        public const int ConnectionRole = 63;
    }

    public sealed class XrmBinding : Binding
    {
        private BindingElementCollection _elements;
        private TransportBindingElement _transportElement;
        private MtomMessageEncodingBindingElement _mtomMessageEncodingElement;
        private TextMessageEncodingBindingElement _textMessageEncodingElement;

        internal XrmBinding(Binding binding)
        {
            if (null == binding)
            {
                throw new ArgumentNullException("binding");
            }

            //Save the binding elements
            this._elements = binding.CreateBindingElements();
            this.Initialize();
        }

        #region Public Methods
        public override BindingElementCollection CreateBindingElements()
        {
            return this._elements.Clone();
        }
        #endregion

        #region Public Properties
        public int MaxBufferSize
        {
            get
            {
                HttpTransportBindingElement element = this._transportElement as HttpTransportBindingElement;
                if (null != element)
                {
                    return element.MaxBufferSize;
                }


                return -1;
            }

            set
            {
                //Set the value on Transport
                HttpTransportBindingElement element = this._transportElement as HttpTransportBindingElement;
                if (null != element)
                {
                    element.MaxBufferSize = value;
                }

                //Set the value on the MTOM Message Encoding
                if (null != this._mtomMessageEncodingElement)
                {
                    this._mtomMessageEncodingElement.MaxBufferSize = value;
                }
            }
        }

        public long MaxReceivedMessageSize
        {
            get
            {
                if (null != this._transportElement)
                {
                    return this._transportElement.MaxReceivedMessageSize;
                }


                return -1;
            }

            set
            {
                if (null != this._transportElement)
                {
                    this._transportElement.MaxReceivedMessageSize = value;
                }
            }
        }

        public XmlDictionaryReaderQuotas ReaderQuotas
        {
            get
            {
                //NOTE: If both the MTOM and Text Message Encoding binding elements exist,
                //and the user changes a value on the value returned from this property,
                //only the TextMessageEncodingBindingElement will be updated. This is not a likely scenario
                if (null != this._textMessageEncodingElement)
                {
                    return this._textMessageEncodingElement.ReaderQuotas;
                }
                else if (null != this._mtomMessageEncodingElement)
                {
                    return this._mtomMessageEncodingElement.ReaderQuotas;
                }

                return null;
            }

            set
            {
                if (null == value)
                {
                    throw new ArgumentNullException("value");
                }

                if (null != this._mtomMessageEncodingElement)
                {
                    value.CopyTo(this._mtomMessageEncodingElement.ReaderQuotas);
                }

                if (null != this._textMessageEncodingElement)
                {
                    value.CopyTo(this._textMessageEncodingElement.ReaderQuotas);
                }
            }
        }

        public override string Scheme
        {
            get
            {
                if (null == this._transportElement)
                {
                    return string.Empty;
                }

                return this._transportElement.Scheme;
            }
        }
        #endregion

        #region Private Methods
        private void Initialize()
        {
            foreach (BindingElement element in this._elements)
            {
                if (null == element)
                {
                    continue;
                }

                //NOTE: There is an implicit assumption that there will only be one of each of these binding element types
                //Check the type of the element
                if (element is TransportBindingElement)
                {
                    this._transportElement = (TransportBindingElement)element;
                }
                else if (element is MtomMessageEncodingBindingElement)
                {
                    this._mtomMessageEncodingElement = (MtomMessageEncodingBindingElement)element;
                }
                else if (element is TextMessageEncodingBindingElement)
                {
                    this._textMessageEncodingElement = (TextMessageEncodingBindingElement)element;
                }
            }
        }
        #endregion
    }
    #endregion
}
//</snippetCrmServiceHelper>